在上一小节,我带你实现了一个简单的 JavaScript AST 解析器,你也应该对词法分析和语法分析底层原理有了一定的了解。那么在本小节的内容中,我们将基于 AST 解析器来实现一个模块打包工具(Bundler),也就是实现一个精简版的 Rollup。通过本节的实战学习,你不仅能对各种 AST 的各种操作信手拈来,还能够对 JavaScript 模块打包工具本身有更加深刻的理解。
# 实现思路梳理
首先我们来梳理一下整体的实现思路,如下图所示:

第一步我们需要获取模块的内容并解析模块 AST,然后梳理模块间的依赖关系,生成一张模块依赖图(ModuleGraph)。
接下来,我们根据模块依赖图生成拓扑排序后的模块列表,以保证最后的产物中各个模块的顺序是正确的,比如模块 A 依赖了模块 B,那么在产物中,模块 B 的代码需要保证在模块 A 的代码之前执行。
当然,Tree Shaking 的实现也是很重要的一环,我会带你实现一个基于 import/export 符号分析的 Tree Shaking 效果,保证只有被 import 的部分被打包进产物。最后,我们便可以输出完整的 Bundle 代码,完成模块打包。
# 开发环境搭建
我们先来搭建一下项目的基本开发环境,首先新建目录my-bundler,然后进入目录中执行 pnpm init -y 初始化,安装一些必要的依赖:
pnpm i magic-string -S
pnpm i @types/node tsup typescript typescript-transform-paths -D
@前端进阶之旅: 代码已经复制到剪贴板
新建tsconfig.json,内容如下:
{
"compilerOptions": {
"target": "es2016",
"allowJs": true,
"module": "commonjs",
"moduleResolution": "node",
"outDir": "dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"sourceMap": true,
"baseUrl": "src",
"rootDir": "src",
"declaration": true,
"plugins": [
{
"transform": "typescript-transform-paths"/* 支持别名 */
},
{
"transform": "typescript-transform-paths",
"afterDeclarations": true/* 支持类型文件中的别名 */
}
],
"paths": {
"*": ["./*"],
"ast-parser": ["../../ast-parser"]/* AST 解析器的路径*/
}
},
"include": ["src"],
"references": [{ "path": "../ast-parser" }]
}
@前端进阶之旅:
